LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

mynote

个人笔记

Vue3中的响应式原理

2022/10/18

vue2.x的响应式:

实现原理:

对象类型:通过Object.definProperty()对属性的读取、修改进行拦截(数据劫持)。

数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹)

Objet.defineProperty(data,'count',{
    get(){},
    set(){}
})

存在问题:

新增属性、删除属性,界面不会更新

直接通过下标修改数组,界面不会自动更新

vue3.x的响应式

实现原理:

通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的增加、属性的删除等

通过Reflect (反射) :对源对象的属性进行操作

模拟vue3中实现响应式

//源数据
let person={
    name:'张三',
    age:18
}
const p=new Proxy(person,{
    //有人读取p的某个属性时调用
    get(target,propName){
    console.log(`有人读取了p身上的${propName}属性`)
    return Reflect.get(target,propName)
    },
    //有人修改p的某个属性、或给p追加某个属性时调用
    set(target,propName,value){
    console.log(`有人修改了了p身上的${propName}属性`)
    Reflect.set(target,propName,value)
    },
    //有人删除p的某个属性时调用
    deleteProperty(target,propName){
    console.log(`有人删除了p身上的${propName}属性`)
    return Reflect.deleteProperty(target,peopName)
    },
})
阅读全文

Ref与reactive

2022/9/26

1. ref

ref 用于为数据添加响应式状态,可以让某一个变量具备响应式的能力。由于reactive只能传入对象类型的参数,而对于基本数据类型要添加响应式状态就只能用ref了,同样返回一个具有响应式状态的副本。

  • 在 js 中使用 ref 的值必须使用 .value 获取,在 Vue 的模板中使用 ref 的值不需要通过 value 获取
  • 语法:const xxx=ref(initValue),创建了一个包含响应式数据的引用对象
  • 接收的数据可以是:基本类型、对象类型,对于基本数据类型,ref是自己的实现方式且性能优于reactive;而对于对象类型,ref仍然是通过reactive包装实现的
  • 参数可以传递任意数据类型,传递对象类型时也能保持深度响应式,所以适用性更广。
  • vue 3.0 setup里定义数据时推荐优先使用ref,方便逻辑拆分和业务解耦。
import { ref } from "vue";

setup() {
    const msg = "张晓明";
    let age = ref(18);
    function add() {
      age.value += 1;
    }
    return { msg, age, add };
}

2. reactive

经过reactive函数处理后的对象能变成响应式的对象,类似于option api里面的data属性的值,它主要是处理你的对象让它经过 Proxy 的加工变为一个响应式的对象。

注意点:

  • 语法:const 代理对象=reactive(源对象),接收一个对象(或数组),返回一个代理对象(proxy对象)

  • 如果想要保持对象内容的响应式能力,在 return 的时候必须把整个 reactive() 对象返回出去

  • 在引用的时候也必须对整个对象进行引用而无法解构,否则这个对象内容的响应式能力将会丢失。

  • reactive()只能包装对象,基本类型不要用它,要用ref函数

  • reactive定义的响应式数据是深层次的,内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作

<template>
  <div class="home">
    名字:{{ state.name }} 价格:{{ state.price }}
    购买数量:{{state.count}}
    <button @click="add">点我加1</button>
  </div>
</template>

<script>
import { ref, reactive } from "vue";

export default {
  name: "Home",
  components: {
  },
  setup() {
    //响应式对象
    const state = reactive({
      count: 0,
      name:"商品1",
      price:100
    });
    //修改响应式对象
    function add() {
      state.count += 1;
    }
    return { state, add };
  },
}
</script>

3. isRef & toRefs & toRef

a) isRef

用来判断某个值是否为ref创建出来的对象。

import { ref, isRef } from 'vue';
export default {
    setup () {
        const count = ref(1);
        const unwrappend = isRef(count) ? count.value : count;
 
        return {
           count,
           unwrappend
        };
    }
}

b) toRefs

但是在具体的业务中,如果无法使用解构取出 reactive() 对象的值,每次都需要通过 state. 操作符访问它里面的属性会是非常麻烦的,所以官方提供了 toRefs() 函数来为我们填好这个坑。只要使用 toRefs()reactive() 对象包装一下,就能够通过解构单独使用它里面的内容了。

toRefs 用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象相应属性的ref。

  • toRefs之后得到的ref引用在获取数据值的时候需要加.value
  • toRefs后的ref数据不是原始数据的拷贝,而是引用,改变结果数据的值也会同时改变原始数据
  • 作用其实和 toRef 类似,只不过 toRef 是一个个手动赋值,而 toRefs 是自动赋值。
<template>
  <div class="home">
    名字:{{ name }} 价格:{{ price }}
    购买数量:{{count}}
    <button @click="add">点我加1</button>
  </div>
</template>

<script>
import { ref, reactive,toRefs } from "vue";

export default {
  name: "Home",
  components: {
  },
  setup() {
    //响应式对象
    const state = reactive({
      count: 0,
      name:"商品1",
      price:100
    });
    //修改响应式对象
    function add() {
      //下面两种写法等效
      state.count += 1;
      // stateRefs.count.value+=1;
    }

    const stateRefs = toRefs(state)

    return { ...stateRefs, add };
  },
}
</script>

c) toRef

概念:为源响应式对象上的某个属性创建一个ref对象,二者内部操作的是同一个数据值,更新时二者是同步的。相当于浅拷贝一个属性。

  • toRef之后得到的ref属性在获取数据值的时候需要加.value
  • toRef后的ref数据不是原始数据的拷贝,而是引用,改变结果数据的值也会同时改变原始数据
<template>
  <div class="home">
    名字:{{ name }} 价格:{{ price }}
    购买数量:{{count}}
    <button @click="add">点我加1</button>
  </div>
</template>

<script>
import { ref, reactive,toRef } from "vue";

export default {
  name: "Home",
  components: {
  },
  setup() {
    //响应式对象
    const state = reactive({
      count: 0,
      name:"商品1",
      price:100
    });
    //修改响应式对象
    function add() {
      //下面两种写法等效
      //state.count += 1;
      count.value += 1;
    }

    const count = toRef(state, "count");

    return { count, add };
  },
}
</script>

d) ref reactive toRefs 和 toRef的比较

**ref : **原始数据没有变化,而ref“包裹的数据”变成了一个新的对象,而且模板有变化(相当于深拷贝)

**reactive : **reactive处理的数据无论是原始数据,“包裹后的数据”,还是模板,都有变化(代理模式)

toRef : toRef处理的数据会有变化,而原始数据也有变化,如果toRef处理是非响应式数据,此时模板没有变化 (相当于浅拷贝,引用关系)

toRefs : toRefs处理的数据响应变化,原始数据也响应变化,如果toRefs处理是非响应式数据,此时模板并没有变化(相当于浅拷贝,引用关系)

ref比较

备注:ref也可以用来定义对象或数组,但内部会自动通过reactive转为代理对象

阅读全文

Setup

2022/9/26

1.理解:vue3.0中一个新的配置项,值为一个函数

2.setup是所有Composition API(组合API)表演的舞台

3.组件中所用到的:数据、方法等等,均要配置在setup中

4.setup函数的两种返回值

​ 1.若要返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。

​ 2.若返回一个渲染函数,则可以自定义渲染内容

5.注意点:

1.尽量不要与vue2.x配置混用

  • vue2.x配置(data、methods、computed…)中可以访问到setup中的属性、方法。
  • 但在setup中不能访问到vue2.x配置(data、methods、computed…)
  • 如果有重名,setup优先

2.setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性。

1 setup

vue3.0将组件的逻辑都写在了函数内部,setup()会取代options API的data()函数,返回一个对象暴露给模板。

setup 选项在组件创建之前执行(所以在setup内部无法通过this来访问组件对象),一旦 props 被解析,就将作为组合式 API 的入口。

setup 函数是个新的入口函数,相当于 vue2.x 中 beforeCreate created,在 beforeCreate 之后 created 之前执行。

setup函数的特性:

  • 使用Composition API 的入口

  • 在beforeCreate之后在created之前执行

  • 在setup中没有this,或者说this是undefined

  • 可以返回一个对象,这个对象的属性被合并到渲染上下文,并可以在模板中直接使用;

  • 接收props对象作为第一个参数,接收来的props对象,包含组件外部传递过来,且组件内部声明接收了的属性,可以通过watch监视其变化。

  • 接受context对象作为第二个参数,这个对象包含attrs,slots,emit三个属性。

    ​ attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs

    ​ slots:收到的插槽内容,相当于this.$slots

    ​ emit:分发的自定义事件函数,相当于this.$emit

阅读全文

Iconfont的使用

2022/8/10

1.在线使用

1.Symbol-在线使用

2.选择Symbol方式引入,复制链接,然后在index.html文件中引入

<script src="https://at.alicdn.com/t/c/font_3572742_w51elmyzq3.js"></script>

3.加入通用css代码(引入一次即可)

.icon {
  width: 1em;
  height: 1em;
  overflow: hidden;
  vertical-align: -0.15em;
  fill: currentColor;
}

我放在了全局通用css文件里

4.使用:挑选相应图标并获取类名,应用于页面

<svg class="icon" aria-hidden="true">
    <use xlink:href="#icon-xxx"></use>
</svg>

2.Symbol-下载本地使用

1.下载至本地

****

2.将其中的iconfont.js放到项目目录下,并在main.js中引入

3.通用css和使用与在线使用相同

阅读全文

Js循环

2022/8/4

for循环

for(初始化变量;条件表达式;操作表达式){

循环体

}

阅读全文

Js流程控制

2022/8/3

流程控制:

if语句:

if(条件表达式){

执行语句

}

如果条件成立则执行,否则什么也不做

if else语句

if(条件表达式){

执行语句

}else{

执行语句

}

条件成立,执行if里面的代码,否则执行else里面的代码

多分支语句

if(条件表达式){

}else if(条件表达式2){

}else

三元表达式:

条件表达式?语句1:语句2

条件表达式成立,值为1,否则为2

条件表达式1?语句1:条件表达式2?语句2:语句3

条件1成立,值为1,否则判断条件2,条件2成立,值为2,否则为3

switch语句

switch(条件表达式){

case value1:

执行语句1;

break;

case value2:

执行语句2;

break;

default:

执行最后的语句

}

当变量设置一系列特定值时,可以使用switch语句

var num=3

switch(num){

case 1:

console.log(1)

break;

}

这里的num和case中的值匹配时必须全等,即num===1

如果当前case中没有break,则不会退出switch,而是继续执行下一个case

阅读全文

算数运算符

2022/8/3

运算符:也被成为操作符,是用于实现赋值、比较和执行算数运算等功能的符号

算数运算符:+、-、*、/、%(取余。9%2=1)

浮点数的最高精度是17位小数,在进行计算时其精度远不如整数,如0.1+0.2=0.30000000000000004,而不是0.3,所以不要直接判断两个浮点数是否相等

递增(++)、递减(–)运算符:根据在变量前还是后分为前置/(后置)递增、递减运算符

前置:先加后用、后置:先用后加,自减同理

比较运算符:

=:赋值

==:判断左右两边值是否相等

===:判断左右两边值和数据类型是否相等

逻辑运算:

短路运算:

逻辑与:表达式1&&表达式2,如果1为真则返回2,如果1为假,则返回1

逻辑或:表达式1||表达式2,如果1为真则返回1,如果1为假则返回2

运算优先级:

阅读全文

Js数据类型

2022/8/3

JavaScript是一种弱类型或者说动态语言,这意味这不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。

数据类型

简单数据类型

Number、Boolean、String、Undefined、Null

js中,8进制前面加0,16进制前面加0x

isNaN():

用来判断一个变量是否为非数字的类型,如果是,返回false,如果不是,返回true

引号嵌套

外双内单,外单内双

typeof:

检测数据类型,typeof null 为object

1.转换为字符串

变量.toString()、String(变量)、+拼接 (变量+’字符串’,隐式转换)

2.转换成数字

  • parseInt:parseInt(变量),转换为数字,得到的是整数(向下取整),parseInt(‘120px’)为120,会自动去掉单位

  • parseFloat:parseFloat(变量),转换为数字,得到的是浮点数,会自动去掉单位

  • Number:Number(变量),

  • 利用算数运算(-,/,*):’12’-0,’12’-‘12’

3.转换为布尔型:

Boolean():代表空、否定的值会被转换为false,如:0,’’,NaN,null,undefined,其余值都会转换为true

阅读全文

Vue-Router

2022/8/1

路由:

1.理解:一个路由(route)就是一组映射关系(key-value),多个路由需要路由器(router进行管理)。

2.前端路由:key是路径,value是组件。

基本使用

1.安装vue-router,命令:npm i vue-router@3(vue2中使用)

2.应用插件:在main.js中引入并应用

import VueRouter from 'vue-router'

Vue.use(VueRouter)

编写router配置项

//引入VueRouter

import VueRout from 'vue-router'

//引入组件

import About from 'xxx'

import Home from 'xxx'

//创建router实例对象,去管理一组一组的路由规则

const router=new VueRouter({

routes:[

{

path:'/about',

component:About

},

{

path:'/home',

component:Home

}

]

})
export default router

const router=new VueRouter({

routes:[

{

path:'/about',

component:()=>imoort ('@/views/xxx')

},

{

path:'/home',

component:()=>imoort ('@/views/xxx')

}

]

})
export default router

切换(ative-class可配置高亮样式)

<router-link active-class="active" to="/about">about</router-link/>

指定展示位置

<router-view></router-link>

注意:

1.路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹

2.通过切换,”隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载

3.每个组件都有自己的$route属性,里面存储着自己的路由信息

4.整个应用只有一个router,可以通过组建的$router属性获取到

多级路由(嵌套路由)

import Messag from 'xxx'
import News from 'xxx'
const router=new VueRouter({

routes:[

{

path:'/home',

component:Home,
children:[
    {
        path:'news',//子路由的path中无需/
        component:News
    },
     {
        path:'message',
        component:Message
    }
]

}

]

})
export default router
<router-link active-class="active" to="/home/news">news</router-link/>//跳转时路径为完整路径(需带着父组件的路径)

路由的query参数

1.传递参数

<router-link :to="`/home/message/detail?id=${item.id}&title=${item.title}`"跳转</router-link>
<router-link :to="{
    path:'/home/message/detail',
    query:{
        id:item.id,
        title:item.title
    }
}"跳转</router-link>

2.接收参数

this.$route.query.id
this.$route.query.title

命名路由

1.作用:简化路由的跳转

2.使用

1.给路由命名

{

path:'/home',

component:Home,
children:[
     {
        path:'news',
        component:News
    },
    children:[
        {
             name:'xiangqing',
             path:'detail',
             component:Detail
        }
    ]
]

}

]
2.简化跳转
//简化前
<router-link to="/home/news/detail">跳转</router-link>
//简化后
<router-link :to="{name:'xiangqing',query:{id:item.id,title,item.title}}">跳转</router-link>

路由的params参数

1.配置路由,声明接受params参数

{
 path:'/home',
 component:Home,
 children:[
     {
        path:'news',
        component:News,
        children:{
             name:'xiangqing',
             path:'detail/:id/:title',//使用占位符声明接受params参数
             component:Detail
        }
     }
 ]
}

2.传递参数

//跳转并携带params参数,to的字符串写法
<router-link :to="/home/message/detail/001/你好">跳转</router-link>
//跳转并携带params参数,to的对象写法
<router-link
:to="{
    name:'xiangqing',
    params:{
        id:001,
        title:'你好'
    }
}"
//使用对象写法时,不能使用path配置项,必须使用name配置

3.接收参数

this.$route.params.id
this.$route.params.title

使用props接收路由参数

{
 path:'/home',
 component:Home,
 children:[
     {
        path:'news',
        component:News,
        children:{
             name:'xiangqing',
             path:'detail/:id/:title',//使用占位符声明接受params参数
             component:Detail,
             props:{a:1,b:'hello'}//props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件,不过数值是死的,固定为a:1,b:'hello'
             props:true,//props的第二种写法,值为布尔值,若其为真,则会把该路由组件接收到的所有params参数,以props的形式传给Detail组件
             props($route){
                 return {id:$route.query.id,title:$route.query.title}//props的第三种写法,值为函数
             }或解构赋值为
                props({$route}){
                 return {id:query.id,title:query.title}//props的第三种写法,值为函数
             }
            
        }
     }
 ]
}

接收:
props:['a','b']
props:['id','title']
props:['id','title']
阅读全文

El-Table隐藏错位问题

ElementUI 2022/7/18

使用el-table组件,两个地方用到了同一个组件,但是展示的页面稍微有些不同,使用v-if进行隐藏,发现出现位置错乱,隐藏失败等问题。解决方法:详阅

这里使用v-show是隐藏不了的,使用v-if,并且给隐藏的列加一个key就可以了

阅读全文
avatar
参商
yyds
img_show